/***************************************************************************
 *   Copyright (C) 2007 by Kai Winter   *
 *   kaiwinter@gmx.de   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "layermanager.h"
namespace qmapcontrol
{
	LayerManager::LayerManager(MapControl* mapcontrol, QSize size)
	:mapcontrol(mapcontrol), scroll(QPoint(0,0)), size(size), whilenewscroll(QPoint(0,0))
	{
// 	genauer berechnen?
		offSize = size + QSize(256*2, 256*2);
		composedOffscreenImage = QPixmap(offSize);
		composedOffscreenImage2 = QPixmap(offSize);
		zoomImage = QPixmap(size);
		zoomImage.fill(QColor(0xf1,0xee,0xe8,0xff));

		screenmiddle = QPoint(size.width()/2, size.height()/2);	
	}


	LayerManager::~LayerManager()
	{
		mylayers.clear();
	}

	QPointF LayerManager::currentCoordinate() const
	{
		return mapmiddle;
	}

	QPixmap LayerManager::getImage() const
	{
		return composedOffscreenImage;
	}

	Layer* LayerManager::layer() const
	{
		Q_ASSERT_X(mylayers.size()>0, "LayerManager::getLayer()", "No layers were added!");
		return mylayers.first();
	}

	Layer* LayerManager::layer(const QString& layername) const
	{
		QListIterator<Layer*> layerit(mylayers);
		while (layerit.hasNext())
		{
			Layer* l = layerit.next();
			if (l->layername() == layername)
				return l;
		}
		return 0;
	}

	QList<QString> LayerManager::layers() const
	{
		QList<QString> keys;
		QListIterator<Layer*> layerit(mylayers);
		while (layerit.hasNext())
		{
			keys.append(layerit.next()->layername());
		}
		return keys;
	}


	void LayerManager::scrollView(const QPoint& point)
	{
// 	if (scrollMutex.tryLock())
		{
		
			scroll += point;
			zoomImageScroll+=point;
			mapmiddle_px += point;
// 	scrollMutex.unlock();
		}
		mapmiddle = layer()->mapadapter()->displayToCoordinate(mapmiddle_px);
		if (!checkOffscreen())
		{
		
			newOffscreenImage();
		}
		else
		{
			moveWidgets();
		}
	}

	void LayerManager::moveWidgets()
	{
		QListIterator<Layer*> it(mylayers);
		while (it.hasNext())
		{
			it.next()->moveWidgets(mapmiddle_px);	
		}
	}

	void LayerManager::setView(const QPointF& coordinate)
	{
		mapmiddle_px = layer()->mapadapter()->coordinateToDisplay(coordinate);
		mapmiddle = coordinate;
	
	//TODO: muss wegen moveTo() raus
		if (!checkOffscreen())
		{
			newOffscreenImage();
		}
		else
		{
		//TODO:
		// verschiebung ausrechnen
		// oder immer neues offscreenimage
			newOffscreenImage();
		}
	}

	void LayerManager::setView(QList<QPointF> coordinates)
	{
		setMiddle(coordinates);
// 	mapcontrol->update();
	}

	void LayerManager::setViewAndZoomIn(const QList<QPointF> coordinates)
	{
		while (containsAll(coordinates))
		{
			setMiddle(coordinates);
			zoomIn();
		}
	
	
		if (!containsAll(coordinates))
		{
			zoomOut();
		}

		mapcontrol->update();
	}

	void LayerManager::setMiddle(QList<QPointF> coordinates)
	{
		int sum_x = 0;
		int sum_y = 0;
		for (int i=0; i<coordinates.size(); i++)
		{
		// mitte muss in px umgerechnet werden, da aufgrund der projektion die mittebestimmung aus koordinaten ungenau ist
			QPoint p = layer()->mapadapter()->coordinateToDisplay(coordinates.at(i));
			sum_x += p.x();
			sum_y += p.y();
		}
		QPointF middle = layer()->mapadapter()->displayToCoordinate(QPoint(sum_x/coordinates.size(), sum_y/coordinates.size()));
	// middle in px rechnen!
	
		setView(middle);
	}

	bool LayerManager::containsAll(QList<QPointF> coordinates) const
	{
		QRectF bb = getViewport();
		bool containsall = true;
		for (int i=0; i<coordinates.size(); i++)
		{
			if (!bb.contains(coordinates.at(i)))
				return false;
		}
		return containsall;
	}
	QPoint LayerManager::getMapmiddle_px() const
	{
		return mapmiddle_px;
	}

	QRectF LayerManager::getViewport() const
	{
		QPoint upperLeft = QPoint(mapmiddle_px.x()-screenmiddle.x(), mapmiddle_px.y()+screenmiddle.y());
		QPoint lowerRight = QPoint(mapmiddle_px.x()+screenmiddle.x(), mapmiddle_px.y()-screenmiddle.y());
		
		QPointF ulCoord = layer()->mapadapter()->displayToCoordinate(upperLeft);
		QPointF lrCoord = layer()->mapadapter()->displayToCoordinate(lowerRight);
	
		QRectF coordinateBB = QRectF(ulCoord, QSizeF( (lrCoord-ulCoord).x(), (lrCoord-ulCoord).y()));
		return coordinateBB;
	}

	void LayerManager::addLayer(Layer* layer)
	{
		mylayers.append(layer);
	
		layer->setSize(size);
	
		connect(layer, SIGNAL(updateRequest(QRectF)),
				  this, SLOT(updateRequest(QRectF)));
		connect(layer, SIGNAL(updateRequest()),
				  this, SLOT(updateRequest()));
	
		if (mylayers.size()==1)
		{
			setView(QPointF(0,0));
		}
	}

	void LayerManager::newOffscreenImage(bool clearImage, bool showZoomImage)
	{
// 	qDebug() << "LayerManager::newOffscreenImage()";
// 	if (refreshMutex.tryLock())
		{
			QPainter painter(&composedOffscreenImage2);
			whilenewscroll = mapmiddle_px;
		
			if (clearImage)
			{
				composedOffscreenImage2.fill(QColor(0xf1,0xee,0xe8,0xff));
			}
			if (showZoomImage)
			{
				painter.drawPixmap(screenmiddle.x()-zoomImageScroll.x(), screenmiddle.y()-zoomImageScroll.y(),zoomImage);
			}
	//only draw basemaps
			for (int i=0; i<mylayers.count(); i++)
			{
				Layer* l = mylayers.at(i);
				if (l->isVisible())
				{
					if (l->layertype() == Layer::MapLayer)
					{
						l->drawYourImage(&painter, whilenewscroll);
					}
				}
			}

			composedOffscreenImage = composedOffscreenImage2;
// 		if (scrollMutex.tryLock())
			{
				scroll = mapmiddle_px-whilenewscroll;
// 			scroll = QPoint(0,0);
// 			scrollMutex.unlock();
			}
			mapcontrol->update();
// 		refreshMutex.unlock();
		}
	
	}

	void LayerManager::zoomIn()
	{
		QCoreApplication::processEvents();
// 	if (getLayer()->getMapAdapter()->getZoom() < getLayer
		ImageManager::instance()->abortLoading();
	// layer rendern abbrechen?
		zoomImageScroll = QPoint(0,0);
	
		zoomImage.fill(QColor(0xf1,0xee,0xe8,0xff));
		QPixmap tmpImg = composedOffscreenImage.copy(screenmiddle.x()+scroll.x(),screenmiddle.y()+scroll.y(), size.width(), size.height());
	
		QPainter painter(&zoomImage);
		painter.save();
		painter.translate(screenmiddle);
		painter.scale(2, 2);
		painter.translate(-screenmiddle);
	
		painter.drawPixmap(0,0,tmpImg);
		painter.restore();
	
		QListIterator<Layer*> it(mylayers);
	//TODO: remove hack, that mapadapters wont get set zoom multiple times
		QList<const MapAdapter*> doneadapters;
		while (it.hasNext())
		{
			Layer* l = it.next();
			if (!doneadapters.contains(l->mapadapter()))
			{
				l->zoomIn();
				doneadapters.append(l->mapadapter());
			}
		}
       // qDebug()<<"mapmiddle"<<mapmiddle;
		mapmiddle_px = layer()->mapadapter()->coordinateToDisplay(mapmiddle);

        //qDebug()<<"map displ"<<mapmiddle_px;
		whilenewscroll = mapmiddle_px;
// 	zoomImageScroll = mapmiddle_px;
		newOffscreenImage();
	
	}

	bool LayerManager::checkOffscreen() const
	{
	// calculate offscreenImage dimension (px)
		QPoint upperLeft = mapmiddle_px - screenmiddle;
		QPoint lowerRight = mapmiddle_px + screenmiddle;
		QRect viewport = QRect(upperLeft, lowerRight);
	
		QRect testRect = layer()->offscreenViewport();

		if (!testRect.contains(viewport))
		{
			return false;
		}
	
		return true;
	}
	void LayerManager::zoomOut()
	{
		QCoreApplication::processEvents();
		ImageManager::instance()->abortLoading();
		zoomImageScroll = QPoint(0,0);
		zoomImage.fill(QColor(0xf1,0xee,0xe8,0xff));
		QPixmap tmpImg = composedOffscreenImage.copy(screenmiddle.x()+scroll.x(),screenmiddle.y()+scroll.y(), size.width(), size.height());
		QPainter painter(&zoomImage);
		painter.translate(screenmiddle);
		painter.scale(0.5,0.5);
		painter.translate(-screenmiddle);
		painter.drawPixmap(0,0,tmpImg);
	
		painter.translate(screenmiddle);
		painter.scale(2,2);
		painter.translate(-screenmiddle);
	
		QListIterator<Layer*> it(mylayers);
	//TODO: remove hack, that mapadapters wont get set zoom multiple times
		QList<const MapAdapter*> doneadapters;
		while (it.hasNext())
		{
			Layer* l = it.next();
			if (!doneadapters.contains(l->mapadapter()))
			{
				l->zoomOut();
				doneadapters.append(l->mapadapter());
			}
		}
		mapmiddle_px = layer()->mapadapter()->coordinateToDisplay(mapmiddle);
		whilenewscroll = mapmiddle_px;
		newOffscreenImage();
	}

	void LayerManager::setZoom(int zoomlevel)
	{
		int current_zoom;
		if (layer()->mapadapter()->minZoom() < layer()->mapadapter()->maxZoom())
		{
			current_zoom = layer()->mapadapter()->currentZoom();
		}
		else
		{
			current_zoom = layer()->mapadapter()->minZoom() - layer()->mapadapter()->currentZoom();
		}
	
	
		if (zoomlevel < current_zoom)
		{
			for (int i=current_zoom; i>zoomlevel; i--)
			{
				zoomOut();
			}
		}
		else
		{
			for (int i=current_zoom; i<zoomlevel; i++)
			{
				zoomIn();
			}
		}
	}

	void LayerManager::mouseEvent(const QMouseEvent* evnt)
	{	
		QListIterator<Layer*> it(mylayers);
		while (it.hasNext())
		{
			Layer* l = it.next();
			if (l->isVisible())
			{
				l->mouseEvent(evnt, mapmiddle_px);
			}
		}	
	}

	void LayerManager::updateRequest(QRectF rect)
	{
		const QPoint topleft = mapmiddle_px - screenmiddle;
		
		QPointF c = rect.topLeft();

		if (getViewport().contains(c) || getViewport().contains(rect.bottomRight()))
		{
// 		QPoint point = getLayer()->getMapAdapter()->coordinateToDisplay(c);
// 		QPoint finalpoint = point-topleft;
// 		QRect rect_px = QRect(int(finalpoint.x()-(rect.width()-1)/2), int(finalpoint.y()-(rect.height()-1)/2),
// 									 int(rect.width()+1), int(rect.height()+1));
			// 		
// 		mapcontrol->updateRequest(rect_px);
			mapcontrol->update();
// 		newOffscreenImage();
		}
	}
	void LayerManager::updateRequest()
	{
		newOffscreenImage();
	}
	void LayerManager::forceRedraw()
	{
		newOffscreenImage();
	}
	void LayerManager::removeZoomImage()
	{
		zoomImage.fill(Qt::white);
		forceRedraw();
	}

	void LayerManager::drawGeoms(QPainter* painter)
	{
		QListIterator<Layer*> it(mylayers);
		while (it.hasNext())
		{
			Layer* l = it.next();
			if (l->layertype() == Layer::GeometryLayer && l->isVisible())
			{
				l->drawYourGeometries(painter, mapmiddle_px, layer()->offscreenViewport());
			}
		}
	}
	void LayerManager::drawImage(QPainter* painter)
	{
		painter->drawPixmap(-scroll.x()-screenmiddle.x(),
									-scroll.y()-screenmiddle.y(),
												 composedOffscreenImage);
	}

	int LayerManager::currentZoom() const
	{
		return layer()->mapadapter()->currentZoom();
	}
}
